//树数据相关操作
import { cloneDeep } from "lodash-es";
import { ID, OrderRule, TableRecord } from "/@/api/models";
//每一层数据的模型
export interface TreeDataItem<T> {
  children?: any;
  checkable?: boolean;
  transfterable?: boolean;
  [key: string]: any;
}

//TODO: 优化类型
// interface NodeItem<T extends TableRecord> {
//   checkable?: boolean;
//   transfterable?: boolean;
//   children?: NodeItem<T>[] | null;
//   [key in T]: any;
// }
// interface TargetItem {
//   id?: string;
//   name?: string;
//   icon?: string;
// }
interface FindItmeOtions<T> {
  keyName?: string;
  parent?: T;
  childrenKey?: string;
}
/**
 * 递归遍历树数据或列表数据，找到key值为keyValue的item，并返回item所在一层的相关信息
 * @param data 树数据或列表数据
 * @param keyValue key的值
 * @returns
 */
export function findItemByKey<T extends TreeDataItem<T>>(
  data: T[],
  keyValue: string | number,
  options?: FindItmeOtions<T>
): { item: T; index: number; items: T[]; parent?: T } | null {
  options = Object.assign<FindItmeOtions<T>, FindItmeOtions<T>>(
    {
      keyName: "id",
      childrenKey: "children",
    },
    options || {}
  );
  for (let i = 0, ii = data.length; i < ii; i++) {
    const item = data[i];
    if (item[options.keyName as string] === keyValue) {
      return {
        item,
        index: i,
        items: data,
        parent: options.parent,
      };
    }
    const chKey = options.childrenKey as string;
    if (item[chKey] && item[chKey].length > 0) {
      options.parent = item;
      const result = findItemByKey(item[chKey], keyValue, options);
      if (result) {
        return result;
      }
    }
  }
  return null;
}

interface LoopItemOptions<T> {
  parent?: T;
  isReverse?: boolean;
  childrenKey?: string;
}
/**
 * 递归遍历树数据，每个节点都执行一次loop回调
 * @param data 树数据
 * @param loop 执行回调，返回false终止遍历
 * @param options
 *  @param parent 当前遍历的层的父节点
 *  @param isReverse 遍历顺序，为true则反向遍历
 *  @param childrenKey 子节点集合key
 * @returns 返回false终止遍历
 */
export function loopItem<T extends TreeDataItem<T>>(
  data: T[],
  loop: (item: T, i: number, items: T[], parent?: T) => boolean | void,
  options?: LoopItemOptions<T>
): boolean | void {
  options = Object.assign<LoopItemOptions<T>, LoopItemOptions<T>>({ isReverse: false }, options || {});
  const len = data.length;
  for (let i = options.isReverse ? len - 1 : 0; options.isReverse ? i >= 0 : i < len; options.isReverse ? i-- : i++) {
    const item = data[i];
    if (loop(item, i, data, options.parent) === false) {
      return false;
    }
    const chKey = options.childrenKey || "children";
    if (item[chKey] && item[chKey].length > 0) {
      options.parent = item;
      const result = loopItem(item[chKey], loop, options);
      if (result === false) {
        return false;
      }
    }
  }
}
interface FindParentsOptions {
  childrenKey?: string;
  keyName?: string;
  keyValue: ID;
}
/**
 * 递归树数据，找到目标节点的所有祖先节点
 * @param data 树数据
 * @param keyValue 目标节点id值
 * @param keyName 目标节点id键名
 * @param parentKeys 找到的所有祖先节点的id值数组
 * @param parentData 找到的所有祖先节点数据
 */
export function findParents<T extends TreeDataItem<T>>(
  data: T[],
  options: FindParentsOptions,
  parentKeys: ID[],
  parentData?: T[]
) {
  options = Object.assign<FindParentsOptions, FindParentsOptions>(
    { keyName: "id", childrenKey: "children", keyValue: options.keyValue },
    options || {}
  );
  for (let i = 0, ii = data.length; i < ii; i++) {
    const item = data[i];
    if (item[options.keyName as string] === options.keyValue) {
      return false;
    }
    const ch = item[options.childrenKey as string];
    if (ch && ch.length > 0) {
      parentKeys.push(item[options.keyName as string]);
      parentData?.push(item);
      if (findParents(ch, options, parentKeys, parentData) === false) {
        return false;
      }
    }
  }
  parentKeys.pop();
  parentData?.pop();
}

export function sortItems<T extends TreeDataItem<T>>(data: T[], sortKey = "sortValue", sortOrder = OrderRule.ASC) {
  data.sort((a, b) => {
    const v1 = a[sortKey] || 0;
    const v2 = b[sortKey] || 0;
    return sortOrder === OrderRule.ASC ? v1 - v2 : v2 - v1;
  });
  for (let i = 0, ii = data.length; i < ii; i++) {
    const item = data[i];
    const ch = item.children;
    if (ch && ch.length > 0) {
      sortItems(ch, sortKey, sortOrder);
    }
  }
}

export function filterTreeDataByIds<T extends TreeDataItem<T>>(data: T[], ids: ID[], keyName = "id") {
  if (data && data.length > 0 && ids.length > 0) {
    const copyData = cloneDeep(data);
    let stayIds: ID[] = [];
    while (ids.length > 0) {
      const targetId = ids[0];
      const pIds: number[] = [];
      findParents(copyData, { keyValue: targetId, keyName }, pIds);
      stayIds.push(targetId);
      stayIds = stayIds.concat(pIds);
      ids.splice(0, 1);
      pIds.forEach((pId) => {
        const index = ids.findIndex((id) => id === pId);
        if (index >= 0) {
          ids.splice(index, 1);
        }
      });
    }
    loopItem(
      copyData,
      (item, i, items) => {
        if (!stayIds.includes(item[keyName])) {
          items.splice(i, 1);
        }
      },
      { isReverse: true }
    );
    return copyData;
  }
  return [];
}
